package com.taobao.diamond.md5;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class MD5
{
  private static final Log log = LogFactory.getLog(MD5.class);
  private static char[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

  private static Map<Character, Integer> rDigits = new HashMap(16);

  private static MD5 me = new MD5();
  private MessageDigest mHasher;
  private ReentrantLock opLock = new ReentrantLock();

  private MD5()
  {
    try {
      this.mHasher = MessageDigest.getInstance("md5");
    }
    catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public static MD5 getInstance()
  {
    return me;
  }

  public String getMD5String(String content)
  {
    return bytes2string(hash(content));
  }

  public String getMD5String(byte[] content)
  {
    return bytes2string(hash(content));
  }

  public byte[] getMD5Bytes(byte[] content)
  {
    return hash(content);
  }

  public byte[] hash(String str)
  {
    this.opLock.lock();
    try {
      byte[] bt = this.mHasher.digest(str.getBytes("GBK"));
      if ((null == bt) || (bt.length != 16)) {
        throw new IllegalArgumentException("md5 need");
      }
      return bt;
    }
    catch (UnsupportedEncodingException e) {
      throw new RuntimeException("unsupported gbk encoding", e);
    }
    finally {
      this.opLock.unlock();
    }
  }

  public byte[] hash(byte[] data)
  {
    this.opLock.lock();
    try {
      byte[] bt = this.mHasher.digest(data);
      if ((null == bt) || (bt.length != 16)) {
        throw new IllegalArgumentException("md5 need");
      }
      return bt;
    }
    finally {
      this.opLock.unlock();
    }
  }

  public String bytes2string(byte[] bt)
  {
    int l = bt.length;

    char[] out = new char[l << 1];

    int i = 0; for (int j = 0; i < l; i++) {
      out[(j++)] = digits[((0xF0 & bt[i]) >>> 4)];
      out[(j++)] = digits[(0xF & bt[i])];
    }

    if (log.isDebugEnabled()) {
      log.debug("[hash]" + new String(out));
    }

    return new String(out);
  }

  public byte[] string2bytes(String str)
  {
    if (null == str) {
      throw new NullPointerException("参数不能为空");
    }
    if (str.length() != 32) {
      throw new IllegalArgumentException("字符串长度必须是32");
    }
    byte[] data = new byte[16];
    char[] chs = str.toCharArray();
    for (int i = 0; i < 16; i++) {
      int h = ((Integer)rDigits.get(Character.valueOf(chs[(i * 2)]))).intValue();
      int l = ((Integer)rDigits.get(Character.valueOf(chs[(i * 2 + 1)]))).intValue();
      data[i] = ((byte)((h & 0xF) << 4 | l & 0xF));
    }
    return data;
  }

  static
  {
    for (int i = 0; i < digits.length; i++)
      rDigits.put(Character.valueOf(digits[i]), Integer.valueOf(i));
  }
}

